<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>vue3 sample</title>
    <link rel="stylesheet" href="assets/css/style.css">
    <script type="text/javascript" src="assets/js/vue3.js"></script>
    <script type="text/javascript" src="assets/js/lodash.min.js"></script>
    <script type="text/javascript" src="assets/js/axios.min.js"></script>
    <link rel="stylesheet" href="assets/css/font-awesome.min.css">

</head>
<body>
    <div>vue3 sample body</a></div>
    <h1>基础</h1>
    <h2>--模板语法</h2>
    <div id="example1" class="demo">
        <p>Using mustaches: {{ rawHtml }}</p>
        <p>Using v-html directive: <span v-html="rawHtml"></span></p>
        <button v-bind:disabled="isButtonDisabled">按钮</button>
        <button @click="setTrue">set true</button>
        <button @click="setFalse">set false</button>
        <button @click="setNull">set null</button>
        <button @click="setUndefined">set undefined</button>
        <div>false, null 或 undefined，那么该 attribute disabled 将不会被包含在渲染的元素上</div>
        <a v-bind:[attributename]="url">baidu</a><button @click="setHref">set href</button>
        <a v-on:[eventname]="doSomething">event name</a><button @click="setFocus">set click on a</button>
        <p>JavaScript 表达式模板表达式都被放在沙盒中，只能访问一个受限的列表'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'</p>
    </div>

    <h2>--Data Property 和方法</h2>
    <div id="counter">
        Counter: {{ counter }}
        <button @click="stop">Stop timer</button>
    </div>
    
    <div id="bind-attribute">
        <span v-bind:title="message">
            鼠标悬停几秒钟查看此处动态绑定的提示信息！
        </span>
    </div>
    <div id="event-handling">
        <p>{{ message }}</p>
        <button v-on:click="reverseMessage">反转 Message</button>
    </div>
    <div id="two-way-binding">
        <p>{{ message }}</p>
        <input v-model="message" />
    </div>

    <div id="conditional-rendering">
        <span v-if="seen">现在你看到我了</span>
    </div>

    <div id="list-rendering">
        <ol>
            <li v-for="todo in todos">
            {{ todo.text }}
            </li>
        </ol>
    </div>
    <div id="lodash">
        <button @click="click">click me by lodash</button>
    </div>
    <div id="lodash_app">
        <save-button></save-button>
    </div>
    <h2>--计算属性和侦听器</h2>
    <div id="computed-basics" class="demo">
        <p>Has published books:</p>
        <span>{{ publishedBooksMessage }}</span>
        <p>{{ calculateBooksMessage() }}</p>
        <p>{{ now }}</p>
        <p>{{ fullName }}</p>
        <button @click="callComputedSet">call computed set</button>
    </div>

    <div id="watch-example" class="demo">
        <p>
            Ask a yes/no question:
            <input v-model="question" />
        </p>
        <p>{{ answer }}</p>
    </div>
    <div id="demo">{{ fullName }}</div>
    <h2>--Class 与 Style 绑定</h2>
    <div id="classStyle1">
        <div :class="classObject">test class</div><button @click="changeClass">changeClass</button>
        <div :class="[isActive ? activeClass : '', errorClass]">test class array</div>
        <div :style="styleObject">test style</div>
        <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">多重值</div>
    </div>
    <h2>--条件渲染</h2>
    <div id="ifshow">
      <div v-if="mytype === 'A'">
        A
      </div>
      <div v-else-if="mytype === 'B'">
        B
      </div>
      <div v-else-if="mytype === 'C'">
        C
      </div>
      <div v-else>
        Not A/B/C
      </div>
      <input type="text" v-model="mytype"/>
      <p>如果需要非常频繁地切换，则使用 v-show 较好；如果在运行时条件很少改变，则使用 v-if 较好。v-show没有else。</p>
    </div>
    <h2>--列表渲染</h2>
    <ul id="array-rendering" class="demo">
      <li v-for="item in items" :key="item.message">
        {{ item.message }}
      </li>
    </ul>

    <ul id="array-with-index" class="demo">
      <li v-for="(item, index) in items">
        {{ parentMessage }} - {{ index }} - {{ item.message }}
      </li>
    </ul>
    <ul id="v-for-object" class="demo">
      <li v-for="value in myObject">
        {{ value }}
      </li>
      <li v-for="(value, name) in myObject">
        {{ name }}: {{ value }}
      </li>
      <li v-for="(value, name, index) in myObject">
        {{ index }}. {{ name }}: {{ value }}
      </li>
    </ul>

    <div id="myfilter" class="demo">
      <ul>
        <li v-for="n in evenNumbers" :key="n">{{ n }}</li>
      </ul>
      <ul v-for="numbers in sets">
        <li v-for="n in even(numbers)" :key="n">{{ n }}</li>
      </ul>
    </div>
    
    <div id="range" class="demo">
      <span v-for="n in 10">{{ n }} </span>
    </div>

    <div id="todo-list-example" class="demo">
      <form v-on:submit.prevent="addNewTodo">
        <label for="new-todo">Add a todo</label>
        <input
          v-model="newTodoText"
          id="new-todo"
          placeholder="E.g. Feed the cat"
        />
        <button>Add</button>
      </form>
      <ul>
        <todo-item
          v-for="(todo, index) in todos"
          v-bind:key="todo.id"
          v-bind:title="todo.title"
          v-on:remove="todos.splice(index, 1)"
        ></todo-item>
      </ul>
    </div>

    <h2>--事件处理</h2>
    <div id="basic-event" class="demo">
      <button @click="counter += 1">Add 1</button>
      <p>The button above has been clicked {{ counter }} times.</p>
    </div>

    <div id="event-with-method" class="demo">
      <!-- `greet` 是在下面定义的方法名 -->
      <button @click="greet">Greet</button>
    </div>

    <div id="inline-handler" class="demo">
      <button @click="say('hi')">Say hi</button>
      <button @click="say('what')">Say what</button>
    </div>
    <div id="dom-event" class="demo">
      <button @click="warn('Form cannot be submitted yet.', $event)">
        Submit
      </button>
      <button @click="one($event), two($event)">
        Two Event Submit
      </button>
      <!-- 点击事件将只会触发一次 -->
      <a @click.once="doThis">click once</a>
      <div @scroll.passive="onScroll">scroll passive...</div>
      <input @keyup.enter="submit" placeholder="enter is submit"/>
      <input @keyup.page-down="onPageDown" placeholder="pagedown is submit"/>
      <!-- Alt + Enter -->
      <input @keyup.alt.enter="onAltEnter" placeholder="alt + enter"/>
      <!-- Ctrl + Click -->
      <div @click.ctrl="doSomething">Ctrl + Click</div>
      <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
      <button @click.ctrl="onHaveCtrlClick">Hava Ctrl</button>
      <!-- 有且只有 Ctrl 被按下的时候才触发 -->
      <button @click.ctrl.exact="onJustHaveCtrlClick">Just have Ctrl</button>
      <!-- 没有任何系统修饰符被按下的时候才触发 -->
      <button @click.exact="onNoneSystemModifiersClick">None System symbal press</button>

      <button @click.left="mouseLeftClick">Mouse Left Click</button>
      <button @click.right="mouseRightClick">Mouse Right Click</button>
      <button @click.middle="mouseMiddleClick">Mouse Middle Click</button>
    </div>

    <h2>--表单输入绑定</h2>
    <div id="v-model-basic" class="demo">
      <input v-model="message" placeholder="edit me" />
      <p>Message is: {{ message }}</p>
      <span>Multiline message is:</span>
      <p style="white-space: pre-line;">{{ message }}</p>
      <br />
      <textarea v-model="message" placeholder="add multiple lines"></textarea>
    </div>
    
    <h2>--组件基础</h2>
    <div id="components-demo">
      <button-counter></button-counter>
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>

    <div id="blog-post-demo" class="demo">
      <blog-post title="My journey with Vue"></blog-post>
      <blog-post title="Blogging with Vue"></blog-post>
      <blog-post title="Why Vue is so fun"></blog-post>
    </div>

    <div id="blog-posts-demo-1">
      <div :style="{ fontSize: postFontSize + 'em' }">
        <blog-post @enlarge-text="onEnlargeText"
          v-for="post in posts"
          :key="post.id"
          :title="post.title"
        ></blog-post>
        
      </div>
      <div>
        在组件上使用 v-model,方法1：<custom-input v-model="searchText"></custom-input>
      </div>
      <div>
        在组件上使用 v-model,方法2：<custom-input-other v-model="searchText"></custom-input-other>
      </div>>
      <p>searchText: {{ searchText }} </p>
    </div>

    <div id="slots-demo" class="demo">
      <alert-box>
        通过插槽分发内容，这里的内容替换插槽！！！
      </alert-box>
    </div>

    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >
        {{ tab }}
      </button>

      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>
    
    <h1>深入组件</h1>
    <h2>--组件注册</h2>
    <div id="reg-component">
      <h4>全局注册</h4>
      <component-a></component-a>
      <component-b></component-b>
      <h4>局部注册</h4>
      <component-c></component-c>
      <component-d></component-d>
    </div>
    <h2>--props</h2>
    <div id="app-prop">
      <component-e title="My journey with Vue" :likes="43" :is-published="false" :comment-ids="[234, 266, 273]" :author="{name: 'Veronica',company: 'Veridian Dynamics'}" 
        :title-var="post.title" :likes-var="post.likes" :is-published-var="post.isPublished" :comment-ids-var="post.commentIds" :author-var="post.author"
        :initial-counter="100" size-str="ADAMS"
        :prop-a="200" prop-b="DEF" prop-c="required" prop-f="success"
        ></component-e>
    </div>

    <h2>--非 Prop 的 Attribute</h2>
    <h3>略</h3>
    <h2>--自定义事件</h2>
    <h3>v-model 参数</h3>
    <div id="customer-vmodel">
      父变量：{{ bookTitle }}
      <my-component v-model:title="bookTitle"></my-component>
    </div>
    <h3>多个 v-model 绑定</h3>
    <div id="multi-vmodel">
      父变量：{{ firstName }} {{ lastName }}
      <user-name
          v-model:first-name="firstName"
          v-model:last-name="lastName"
        ></user-name>
    </div>
    <h3>处理 v-model 修饰符</h3>
    <div id="vmodel-modifier">
      <my-component v-model.capitalize="myText"></my-component>  
    </div>
    <h4>首字母大写</h4>
    <div id="vmodel-modifier-1">
      <my-component v-model.capitalize="myText"></my-component>
      {{ myText }}
    </div>
    <h4>description首字母大写</h4>
    <div id="vmodel-modifier-2">
      <my-component v-model:description.capitalize="myText"></my-component>
    </div>
    <h4>对于带参数的 v-model 绑定，生成的 prop 名称将为 arg + "Modifiers"; example</h4>
    <div id="vmodel-modifier-ex">
      <my-component 
        v-model.toUpperCase="message1" 
        v-model:text.toLowerCase="message2" 
      >
      </my-component>
      <div>绑定输入的字母全为大写：{{ message1 }}</div>
      <div>绑定输入的字母全为小写：{{ message2 }}</div>
    </div>
    <h2>--插槽</h2>
    <h3>备用内容</h3>
    <div id="slot-bk">
      <submit-button>save</submit-button>
      <submit-button/>
    </div>
    <h3>具名插槽</h3>
    <div id='named-slot'>
      <base-layout>
        <template v-slot:header>
          <h1>Here might be a page title</h1>
        </template>

        <template v-slot:default>
          <p>A paragraph for the main content.</p>
          <p>And another one.</p>
        </template>

        <template v-slot:footer>
          <p>Here's some contact info</p>
        </template>
      </base-layout>
    </div>
    <h3>作用域插槽</h3>
    <div id="scope-slot">
      <todo-list>
        <template v-slot:default="slotProps">
          <i class="fa fa-camera-retro"></i>
          <span style="color: green">{{ slotProps.item }}</span>
        </template>
      </todo-list>
      独占默认插槽的缩写语法<br>
      等同于
      <todo-list v-slot:default="slotProps">
        <i class="fa fa-camera-retro"></i>
        <span style="color: blue">{{ slotProps.item }}</span>
      </todo-list>
      等同于
      <todo-list v-slot="slotProps">
        <i class="fa fa-camera-retro"></i>
        <span style="color: red">{{ slotProps.item }}</span>
      </todo-list>
      只要出现多个插槽，请始终为所有的插槽使用完整的基于 template 的语法
      <h3>解构插槽 Prop</h3>
      <todo-list v-slot="{ item }">
        <i class="fa fa-camera-retro"></i>
        <span style="color: red">{{ item }}</span>
      </todo-list>
      <todo-list v-slot="{ item: todo }">
        <i class="fa fa-camera-retro"></i>
        <span style="color: red">{{ todo }}</span>
      </todo-list>
    </div>
    <h3>动态插槽名</h3>
    <h4>略</h4>
    <h3>具名插槽的缩写</h3>
    <h4>略</h4>
    <h2>--Provide / Inject</h2>
    父组件给两层以下的子组件传数据，example略
    <h2>--动态组件 & 异步组件</h2>
    <div id="dynamic-component-demo-1" class="demo">
      <button
        v-for="tab in tabs"
        :key="tab"
        :class="['tab-button', { active: currentTab === tab }]"
        @click="currentTab = tab"
      >
        {{ tab }}
      </button>

      <keep-alive>
        <component :is="currentTabComponent" class="tab"></component>
      </keep-alive>
    </div>
    <div>异步组件按需要加载，参考defineAsyncComponent</div>
    <h2>--模板引用</h2>
    <div id="template-ref">
      <base-input></base-input>
    </div>
    <h2>--处理边界情况</h2>
    <h4>略</h4>
</body>
<script>
  const RenderHtmlApp = {
    data() {
      return {
        rawHtml: '<span style="color: red">This should be red.</span>',
        isButtonDisabled: true,
        attributename: "title",
        url: "http://www.baidu.com",
        eventname: "focus"
      }
    },
    methods: {
      setTrue() {
        this.isButtonDisabled = true
      },
      setFalse() {
        this.isButtonDisabled = false
      },
      setNull() {
        this.isButtonDisabled = null
      },
      setUndefined() {
        this.isButtonDisabled = undefined
      },
      setHref() {
        console.log("set href")
        this.attributename = "href"
      },
      setFocus() {
        console.log("set focus")
        this.eventname = "click"
      },
      doSomething() {
        console.log("do something")
      }
    }
  }

  Vue.createApp(RenderHtmlApp).mount('#example1')

  const Counter = {
    data() {
      return {
        counter: 0
      }
    },
    mounted() {
      interval = setInterval(() => {
        this.counter++
      }, 1000)
    },
    methods: {
      stop() {
        console.log("stop interval");
        clearInterval(interval);
      }
    }
}

Vue.createApp(Counter).mount('#counter')

const AttributeBinding = {
  data() {
    return {
      message: 'You loaded this page on ' + new Date().toLocaleString()
    }
  }
}

Vue.createApp(AttributeBinding).mount('#bind-attribute')

const EventHandling = {
  data() {
    return {
      message: 'Hello Vue.js!'
    }
  },
  methods: {
    reverseMessage() {
      this.message = this.message
        .split('')
        .reverse()
        .join('')
    }
  }
}

Vue.createApp(EventHandling).mount('#event-handling')

const TwoWayBinding = {
  data() {
    return {
      message: 'Hello Vue!'
    }
  }
}

Vue.createApp(TwoWayBinding).mount('#two-way-binding')

const ConditionalRendering = {
  data() {
    return {
      seen: true
    }
  }
}

Vue.createApp(ConditionalRendering).mount('#conditional-rendering')

const ListRendering = {
  data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' }
      ]
    }
  }
}

Vue.createApp(ListRendering).mount('#list-rendering')

Vue.createApp({
    methods: {
      // 用 Lodash 的防抖函数
      click: _.debounce(function() {
        // ... 响应点击 ...
        console.log("click")
      }, 500)
    }
  }).mount('#lodash')

const lodash_app = Vue.createApp({})

lodash_app.component('save-button', {
  created() {
    // 用 Lodash 的防抖函数
    this.debouncedClick = _.debounce(this.click, 500)
  },
  unmounted() {
    // 移除组件时，取消定时器
    this.debouncedClick.cancel()
  },
  methods: {
    click() {
      // ... 响应点击 ...
      console.log("component click")
    }
  },
  template: `
    <button @click="debouncedClick">
      Save
    </button>
  `
})
lodash_app.mount('#lodash_app')

// 计算属性和侦听器
Vue.createApp({
  data() {
    return {
      author: {
        name: 'John Doe',
        books: ['Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery']
      },
      firstName: 'test',
      lastName: 'adams'
    }
  },
  computed: {
    // a computed getter
    publishedBooksMessage() {
      // `this` points to the vm instance
      return this.author.books.length > 0 ? 'Yes' : 'No'
    },
    now() {
      return Date.now()
    },
    fullName: {
        // getter
        get() {
        return this.firstName + ' ' + this.lastName
        },
        // setter
        set(newValue) {
        const names = newValue.split(' ')
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
        }
    }
  },
  methods: {
    calculateBooksMessage() {
        return this.author.books.length > 0 ? 'Yes' : 'No'
    },
    callComputedSet() {
        this.fullName = 'John Doe'
    }
  }
}).mount('#computed-basics')

const watchExampleVM = Vue.createApp({
  data() {
    return {
      question: '',
      answer: 'Questions usually contain a question mark. ;-)'
    }
  },
  watch: {
    // whenever question changes, this function will run
    question(newQuestion, oldQuestion) {
      if (newQuestion.indexOf('?') > -1) {
        this.getAnswer()
      }
    }
  },
  methods: {
    getAnswer() {
      this.answer = 'Thinking...'
      axios
        .get('https://yesno.wtf/api')
        .then(response => {
        this.answer = response.data.answer
      })
        .catch(error => {
        this.answer = 'Error! Could not reach the API. ' + error
      })
    }
  }
}).mount('#watch-example')

const vm = Vue.createApp({
  data() {
    return {
      firstName: 'Foo',
      lastName: 'Bar'
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}).mount('#demo')

Vue.createApp({
  data() {
    return {
      /*
      classObject: {
          active: true,
          'text-danger': false
      }
      */
      isActive: true,
      error: null,
      activeClass: 'active',
      errorClass: 'text-danger',
      styleObject: {
        color: 'red',
        fontSize: '20px'
      }
    }
  },
  computed: {
    classObject() {
        return {
        active: this.isActive && !this.error,
        'text-danger': this.error && this.error === 'fatal'
        }
    }
  },
  methods: {
      changeClass() {
          this.isActive = !this.isActive
          if (this.error != null && this.error != ""){
              console.log("set empty")
              this.error = ""
          }else {
              console.log("set fatal")
              this.error = "fatal"
          }
      }
  }
}).mount('#classStyle1')

Vue.createApp({
  data() {
    return {
     mytype: "A"
    }
  }
}).mount('#ifshow')

// 列表渲染
Vue.createApp({
  data() {
    return {
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }
}).mount('#array-rendering')

Vue.createApp({
  data() {
    return {
      parentMessage: 'Parent',
      items: [{ message: 'Foo' }, { message: 'Bar' }]
    }
  }
}).mount('#array-with-index')

Vue.createApp({
  data() {
    return {
      myObject: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2020-03-22'
      }
    }
  }
}).mount('#v-for-object')

Vue.createApp({
  data() {
    return {
      numbers: [ 1, 2, 3, 4, 5 ],
      sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
    }
  },
  computed: {
    evenNumbers() {
      return this.numbers.filter(number => number % 2 === 0)
    }
  },
  methods: {
  even(numbers) {
    return numbers.filter(number => number % 2 === 0)
  }
}
}).mount('#myfilter')


Vue.createApp({}).mount('#range')

const app = Vue.createApp({
  data() {
    return {
      newTodoText: '',
      todos: [
        {
          id: 1,
          title: 'Do the dishes'
        },
        {
          id: 2,
          title: 'Take out the trash'
        },
        {
          id: 3,
          title: 'Mow the lawn'
        }
      ],
      nextTodoId: 4
    }
  },
  methods: {
    addNewTodo() {
      this.todos.push({
        id: this.nextTodoId++,
        title: this.newTodoText
      })
      this.newTodoText = ''
    }
  }
})

app.component('todo-item', {
  props: ['title'],
  emits: ['remove'],
  template: `
          <li>
            {{ title }}
            <button v-on:click="$emit('remove')">Remove</button>
          </li>
        `,
  
})
app.mount('#todo-list-example')

Vue.createApp({
  data() {
    return {
      counter: 0
    }
  }
}).mount('#basic-event')

Vue.createApp({
  data() {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    greet(event) {
      // `methods` 内部的 `this` 指向当前活动实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}).mount('#event-with-method')

Vue.createApp({
  methods: {
    say(message) {
      alert(message)
    }
  }
}).mount('#inline-handler')

Vue.createApp({
  methods: {
    warn(message, event) {
      // 现在可以访问到原生事件
      if (event) {
        event.preventDefault()
      }
      alert(message)
    },
    one(event) {
      // 第一个事件处理器逻辑...
      alert("one")
    },
    two(event) {
      // 第二个事件处理器逻辑...
      alert("two")
    },
    doThis() {
      alert("display once")
    },
    onScroll() {
      alert("onScroll")
    },
    submit() {
      console.log("submit")
    },
    onPageDown() {
      console.log("on page down")
    },
    onAltEnter() {
      console.log("on Alt Enter")
    },
    doSomething() {
      alert("do something")
    },
    onHaveCtrlClick() {
      alert("onHaveCtrlClick")
    },
    onJustHaveCtrlClick() {
      alert("onJustHaveCtrlClick")
    },
    onNoneSystemModifiersClick() {
      alert("onNoneSystemModifiersClick")
    },
    mouseLeftClick() {
      alert("mouseLeftClick")
    },
    mouseRightClick() {
      alert("mouseRightClick")
    },
    mouseMiddleClick() {
      alert("mouseMiddleClick")
    }
  }
}).mount('#dom-event')

Vue.createApp({
  data() {
    return {
      message: ''
    }
  }
}).mount('#v-model-basic')

// 创建一个Vue 应用
const app_component_1 = Vue.createApp({})

// 定义一个名为 button-counter 的新全局组件
app_component_1.component('button-counter', {
  data() {
    return {
      count: 0
    }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
})

app_component_1.mount('#components-demo')

const app_blog_post_demo = Vue.createApp({})

app_blog_post_demo.component('blog-post', {
  props: ['title'],
  template: `<h4>{{ title }}</h4>`
})

app_blog_post_demo.mount('#blog-post-demo')


const App_blog_posts_demo_1 = {
  data() {
    return {
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ],
      postFontSize: 1,
      searchText: "searchText"
    }
  },
  methods: {
    onEnlargeText(enlargeAmount) {
      this.postFontSize += enlargeAmount
    }
  }
}

const app_blog_posts_demo_1 = Vue.createApp(App_blog_posts_demo_1)

app_blog_posts_demo_1.component('blog-post', {
  props: ['title'],
  emits: ['enlargeText'],
  template: `
    <div class="blog-post">
      <h4>{{ title }}</h4>
      <button @click="$emit('enlargeText', 0.1)">
        Enlarge text
      </button>
    </div>
  `
})

app_blog_posts_demo_1.component('custom-input', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})

app_blog_posts_demo_1.component('custom-input-other', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    <input v-model="value">
  `,
  computed: {
    value: {
      get() {
        return this.modelValue
      },
      set(value) { 
        this.$emit('update:modelValue', value)
      }
    }
  }
})

app_blog_posts_demo_1.mount('#blog-posts-demo-1')

const app_slot = Vue.createApp({})

app_slot.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

app_slot.mount('#slots-demo')

const app_dynamic = Vue.createApp({
  data() {
    return {
      currentTab: 'Home',
      tabs: ['Home', 'Posts', 'Archive']
    }
  },
  computed: {
    currentTabComponent() {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})

app_dynamic.component('tab-home', {
  template: `<div class="demo-tab">Home component</div>`
})
app_dynamic.component('tab-posts', {
  template: `<div class="demo-tab">Posts component</div>`
})
app_dynamic.component('tab-archive', {
  template: `<div class="demo-tab">Archive component</div>`
})

app_dynamic.mount('#dynamic-component-demo')

// 全局注册，局部注册C
const ComponentC = {
  template: `<div>component C</div>`
}
const ComponentD = {
  components: {
    'component-c': ComponentC
  },
  template: `<div>component D</div> C is in the D<component-c></component-c>`
}
const app_reg_component = Vue.createApp({
  components: {
    'component-c': ComponentC,
    'component-d': ComponentD
  }

})
app_reg_component.component('component-a', {
  template: `<div>component A</div>`
})
app_reg_component.component('component-b', {
  template: `<div>component B</div>A is in the B<component-a></component-a>`
})
app_reg_component.mount('#reg-component')

// Prop 类型
const ComponentE = {
  props:{
    title: String,
    titleVar: String,
    likes: Number,
    likesVar: Number,
    isPublished: Boolean,
    isPublishedVar: Boolean,
    commentIds: Array,
    commentIdsVar: Array,
    author: Object,
    authorVar: Object,
    callback: Function,
    contactsPromise: Promise, // 或任何其他构造函数,
    initialCounter: Number,
    sizeStr: String,

    // 基础的类型检查 (`null` 和 `undefined` 值会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组的默认值必须从一个工厂函数返回
      default() {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator(value) {
        // 这个值必须与下列字符串中的其中一个相匹配
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 具有默认值的函数
    propG: {
      type: Function,
      // 与对象或数组的默认值不同，这不是一个工厂函数——这是一个用作默认值的函数
      default() {
        return 'Default function'
      }
    },

  },
  data() {
    return {
      counter: this.initialCounter
    }
  },
  template: 
    `<div>title: {{ title }}; titleVar: {{ titleVar }}; likes: {{ likes }}; likesVar: {{ likesVar }};
    isPublished: {{ isPublished }}; isPublishedVar: {{ isPublishedVar }};
    commentIds: <div v-for="commentId in commentIds" :key="commentId">{{ commentId }}</div>;
    commentIds_var: <div v-for="commentIdVar in commentIdsVar" :key="commentIdVar">{{ commentIdVar }}</div>;
    author: {{ author.name }} {{ author.company }}; author_var: {{ authorVar.name}} {{ authorVar.company }}<br>
    <h5>这个 prop 用来传递一个初始值；这个子组件接下来希望将其作为一个本地的 prop 数据来使用</h5>
    {{ counter }}<br>
    <h5>这个 prop 以一种原始的值传入且需要进行转换。在这种情况下，最好使用这个 prop 的值来定义一个计算属性</h5>
    {{ normalizedSize }}<br>
    <h5>Prop 验证</h5>
    {{ propA }};{{ propB }};{{ propC }};{{ propD }};{{ propE }};{{ propF }};{{ propG }};
    </div>`,
  computed: {
    normalizedSize() {
      return this.sizeStr.trim().toLowerCase()
    }
  }
}

const app_prop = Vue.createApp({
  data() {
    return {
      post: {
        title: 'message(post.title)',
        likes: 46,
        isPublished: false,
        commentIds: ["aaa", "bbb", "ccc"],
        author: { name: 'Adams', company: 'adams co'}
      }
    }
  },
  components: {
    'component-e': ComponentE
  }
})
app_prop.mount('#app-prop')

// v-model 参数
const app_customer_vmodel = Vue.createApp({
  data() {
    return {
      bookTitle: 'test book title'
    }
  }
})

app_customer_vmodel.component('my-component', {
  props: {
    title: String
  },
  emits: ['update:title'],
  template: `
    <input
      type="text"
      :value="title"
      @input="$emit('update:title', $event.target.value)">子变量：{{ title }}
  `
})

app_customer_vmodel.mount('#customer-vmodel')

// 多个 v-model 绑定
const app_multi_vmodel = Vue.createApp({
  data() {
    return {
      firstName: 'test fristName',
      lastName: 'test lastName',
    }
  }
})
app_multi_vmodel.component('user-name', {
  props: {
    firstName: String,
    lastName: String
  },
  emits: ['update:firstName', 'update:lastName'],
  template: `
    <input 
      type="text"
      :value="firstName"
      @input="$emit('update:firstName', $event.target.value)">

    <input
      type="text"
      :value="lastName"
      @input="$emit('update:lastName', $event.target.value)">
    子变量：{{ firstName }} {{ lastName }}
  `
})
app_multi_vmodel.mount('#multi-vmodel')

// 处理 v-model 修饰符
const app_vmodel_modifier = Vue.createApp({
  data() {
    return {
      myText: '1122'
    }
  }
})

app_vmodel_modifier.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  template: `
    <input type="text"
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)">
  `,
  created() {
    console.log(this.modelModifiers) // { capitalize: true }
  }
})

app_vmodel_modifier.mount('#vmodel-modifier')

// 首字母大写
const app_vmodel_modifier_1 = Vue.createApp({
  data() {
    return {
      myText: 'abc'
    }
  }
})

app_vmodel_modifier_1.component('my-component', {
  props: {
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
  },
  emits: ['update:modelValue'],
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.modelModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit('update:modelValue', value)
    }
  },
  template: `<input
    type="text"
    :value="modelValue"
    @input="emitValue">`
  
})

app_vmodel_modifier_1.mount('#vmodel-modifier-1')

// description首字母大写
const app_vmodel_modifier_2 = Vue.createApp({
  data() {
    return {
      myText: 'def'
    }
  }
})
app_vmodel_modifier_2.component('my-component', {
  props: ['description', 'descriptionModifiers'],
  emits: ['update:description'],
  template: `
    <input type="text"
      :value="description"
      @input="$emit('update:description', $event.target.value)">
  `,
  created() {
    console.log(this.descriptionModifiers) // { capitalize: true }
  }
})

app_vmodel_modifier_2.mount('#vmodel-modifier-2')

// 对于带参数的 v-model 绑定，生成的 prop 名称将为 arg + "Modifiers", example
const app_vmodel_modifier_ex = Vue.createApp({
  data() {
    return {
      message1: '',
      message2: ''
    }
  }
})

app_vmodel_modifier_ex.component('my-component', {
  props:['modelValue','modelModifiers','text','textModifiers'],
  
  template:`
    <div>
      <input 
        type="text" 
        :value="modelValue" 
        @input="handleInput1"
      >
      <input 
        type="text" 
        :value="text" 
        @input="handleInput2"
      >
    </div>
  `,
  
  created(){
    //对不带参数的v-model绑定,检查对象名称为：modelModifiers
    //对于带prop参数的v-model绑定，检查对象名称为：prop + modelModifiers
    console.log(this.modelModifiers); //{toUpperCase: true}
    console.log(this.textModifiers); //{toLowerCase: true}
  },

  methods: {
    handleInput1(e){
      let value = e.target.value;
      console.log(this.modelModifiers.touppercase);
      if(this.modelModifiers.touppercase){
        value = value.toUpperCase();
      }
      this.$emit("update:modelValue", value)
    },
    handleInput2(e){
      let value = e.target.value;
      console.log(this.textModifiers.tolowercase);
      if(this.textModifiers.tolowercase){
        value = value.toLowerCase();
      }
      this.$emit("update:text", value)
    }
  }
})

app_vmodel_modifier_ex.mount('#vmodel-modifier-ex')

// 插槽
// 备用内容
const app_slot_bk= Vue.createApp({})

app_slot_bk.component('submit-button', {
  template: `
  <button type="submit">
    <slot>submit</slot>
  </button>
  `
})

app_slot_bk.mount('#slot-bk')

// 具名插槽
const app_named_slot= Vue.createApp({})

app_named_slot.component('base-layout', {
  template: `
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
  `
})

app_named_slot.mount('#named-slot')

// 作用域插槽
const app_scope_slot= Vue.createApp({})

app_scope_slot.component('todo-list', {
  data() {
    return {
      items: ['Feed a cat', 'Buy milk']
    }
  },

  template: `
    <ul>
      <li v-for="( item, index ) in items">
        <slot :item="item" :index="index"></slot>
      </li>
    </ul>
  `
})

app_scope_slot.mount('#scope-slot')

// 模板引用
const app_template_ref = Vue.createApp({})

app_template_ref.component('base-input', {
  template: `
    <input ref="input" />
  `,
  methods: {
    focusInput() {
      this.$refs.input.focus()
    }
  },
  mounted() {
    this.focusInput()
  }
})
app_template_ref.mount('#template-ref')

// 动态组件 & 异步组件
const app_keep_alive = Vue.createApp({
  data() {
    return {
      currentTab: 'Home',
      tabs: ['Home', 'Posts', 'Archive']
    }
  },
  computed: {
    currentTabComponent() {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})

app_keep_alive.component('tab-home', {
  template: `<div class="demo-tab">Home component</div>`
})
app_keep_alive.component('tab-posts', {
  template: `<div class="dynamic-component-demo-posts-tab">
    <ul class="dynamic-component-demo-posts-sidebar">
      <li
        v-for="post in posts"
        :key="post.id"
        :class="{
          'dynamic-component-demo-active': post === selectedPost
        }"
        @click="selectedPost = post"
      >
        {{ post.title }}
      </li>
    </ul>
    <div class="dynamic-component-demo-post-container">
      <div v-if="selectedPost" class="dynamic-component-demo-post">
        <h3>{{ selectedPost.title }}</h3>
        <div v-html="selectedPost.content"></div>
      </div>
      <strong v-else>
        Click on a blog title to the left to view it.
      </strong>
    </div>
  </div>`,
  data() {
    return {
      posts: [
        {
          id: 1,
          title: 'Cat Ipsum',
          content:
            '<p>Dont wait for the storm to pass, dance in the rain kick up litter decide to want nothing to do with my owner today demand to be let outside at once, and expect owner to wait for me as i think about it cat cat moo moo lick ears lick paws so make meme, make cute face but lick the other cats. Kitty poochy chase imaginary bugs, but stand in front of the computer screen. Sweet beast cat dog hate mouse eat string barf pillow no baths hate everything stare at guinea pigs. My left donut is missing, as is my right loved it, hated it, loved it, hated it scoot butt on the rug cat not kitten around</p>'
        },
        {
          id: 2,
          title: 'Hipster Ipsum',
          content:
            '<p>Bushwick blue bottle scenester helvetica ugh, meh four loko. Put a bird on it lumbersexual franzen shabby chic, street art knausgaard trust fund shaman scenester live-edge mixtape taxidermy viral yuccie succulents. Keytar poke bicycle rights, crucifix street art neutra air plant PBR&B hoodie plaid venmo. Tilde swag art party fanny pack vinyl letterpress venmo jean shorts offal mumblecore. Vice blog gentrify mlkshk tattooed occupy snackwave, hoodie craft beer next level migas 8-bit chartreuse. Trust fund food truck drinking vinegar gochujang.</p>'
        },
        {
          id: 3,
          title: 'Cupcake Ipsum',
          content:
            '<p>Icing dessert soufflé lollipop chocolate bar sweet tart cake chupa chups. Soufflé marzipan jelly beans croissant toffee marzipan cupcake icing fruitcake. Muffin cake pudding soufflé wafer jelly bear claw sesame snaps marshmallow. Marzipan soufflé croissant lemon drops gingerbread sugar plum lemon drops apple pie gummies. Sweet roll donut oat cake toffee cake. Liquorice candy macaroon toffee cookie marzipan.</p>'
        }
      ],
      selectedPost: null
    }
  }
})
app_keep_alive.component('tab-archive', {
  template: `<div class="demo-tab">Archive component</div>`
})

app_keep_alive.mount('#dynamic-component-demo-1')
</script>
</html>